#include <fstream>

template<int MAX_SIZE = 102400, int MIN_READ = 1024>
class FileCache {
 private:
  uint8_t _buf[MAX_SIZE];
  size_t _buf_len;

  const uint8_t *_data;

  std::ifstream _fin;

 public:
  FileCache(void) {
    _buf_len = 0;
    _data = _buf;
  }

  int open(const std::string &fpath) {
    _fin.open(fpath, std::ios_base::binary);
    return _fin.is_open() ? 0 : -1;
  }

  int seek(off_t pos) {
    _fin.clear();
    _fin.seekg(pos);
    if (_fin.fail()) {
      return -1;
    }
    _buf_len = 0;
    _data = _buf;
    return 0;
  }

  int read(size_t count, const uint8_t *&data, size_t &len) {
    size_t avail = _buf_len - (_data - _buf);
    if (count <= avail) {
      data = _data;
      len = count;
      return 0;
    } else {
      if (!_fin.eof()) {
        size_t read = std::min(std::max(count - avail, (size_t)MIN_READ), (size_t)MAX_SIZE - avail);
        if (_buf_len + read > MAX_SIZE) {
          std::copy(_data, _data + avail, _buf);
          _data = _buf;
          _buf_len = avail;
        }
        _fin.read((char *)_buf + _buf_len, read);
        if (_fin.bad()) {
          return -1;
        }
        _buf_len += _fin.gcount();
        assert(_buf_len <= MAX_SIZE);
        avail += _fin.gcount();
      }
      data = _data;
      len = std::min(avail, count);
      return (len == 0) ? 1 : 0;
    }
  }

  int drop(size_t count) {
    size_t avail = _buf_len - (_data - _buf);
    if (count <= avail) {
      _data += count;
      return 0;
    } else {
      return -1;
    }
  }

};


class DataCache {
 private:
  std::list<std::vector<uint8_t>> _buf;
  size_t _prestored = 0, _confirmed = 0;

 public:
  void prestore(const uint8_t *data, size_t len) {
    _buf.emplace_back(data, data + len);
    _prestored += len;
  }

  size_t confirm(size_t len) {
    size_t ret = std::min(len, _prestored - _confirmed);
    _confirmed += ret;
    return ret;
  }

  size_t unconfirmed(void) {
    return _prestored - _confirmed;
  }

  std::unique_ptr<uint8_t[]> get(size_t &len) {
    std::unique_ptr<uint8_t[]> ret(new uint8_t[_confirmed]);
    len = 0;
    for (auto &d : _buf) {
      size_t count = std::min(d.size(), _confirmed - len);
      if (count > 0) {
        std::copy(d.data(), d.data() + count, ret.get() + len);
        len += count;
      }
    }
    clear();
    return ret;
  }

  void clear(void) {
    _buf.clear();
    _prestored = 0;
    _confirmed = 0;
  }

};
